-
Notifications
You must be signed in to change notification settings - Fork 5k
Design document for Objective-C interop #44934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Design document for Objective-C interop #44934
Conversation
Thanks @AaronRobinsonMSFT for the heads up : ) I am about to be on vacation for end of the year but I can take a closer look when I am back or @tommcdon might ask one of the other devs to look into it. I'm assuming this has a generic .NET 6 timeline unless you say otherwise? |
* Hardcode these special cases in the runtime with the assembly code. | ||
* Hardcode these special cases in the runtime but permit the "overrides" to be provided through a managed API. | ||
|
||
The last option seems to have the most flexibility and follows the "pay-for-play" principle since some users may not care about exception handling or variadic argument support. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does PINVOKE_OVERRIDE
fit into this vs the approach in #47721? There is quite an overlap.
PINVOKE_OVERRIDE
could easily be used by the native code to implement the same logic as mono_dllmap_insert
used. It requires support from the host though.
I am not convinced that a managed interface is very helpful in this case. The objc_*
methods themselves still have to be implemented in the native code due to the nature of the argument dispatch. Who would register the callbacks and at what time would that happen? If the answer is that it's done once at startup and driven by the host then I think the approach is unnecessarily complicated and duplicates the functionality that is already available through other means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PINVOKE_OVERRIDE
logic was leveraged to implement this feature. The overlap here is related to implementation detail only since the PINVOKE_OVERRIDE
feature isn't publicly exposed. There is no desire to presently expose that and thus we permit a narrow override for this specific scenario. I am assuming that PINVOKE_OVERRIDE
is referring to what is used to handle single file work.
I am not convinced that a managed interface is very helpful in this case.
CoreCLR is not exposing functionality through native means. Using UnmanagedCallersOnly
and function pointers exposing most functionality to native can be done in a bespoke manner based on a consumer's needs.
I am not convinced that a managed interface is very helpful in this case. The objc_* methods themselves still have to be implemented in the native code due to the nature of the argument dispatch.
They don't actually need to be implemented in native at all depending on what one wants to do with them. We have various tests that implement them in managed code using UnmanagedCallersOnly
. This isn't to say we can handle all functionality but most is possible at least with what we've prototyped - perhaps there is an example?
Who would register the callbacks and at what time would that happen? If the answer is that it's done once at startup and driven by the host then I think the approach is unnecessarily complicated and duplicates the functionality that is already available through other means.
Yep, this must be done relatively early.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PINVOKE_OVERRIDE
is for unmanaged hosts. It cannot be really replaced by managed code for all cases. The host cannot always inject the managed code soon enough for it to kick in.
Our original design goal was to make Objective-C interop work without special host. For example, to allow using libraries that use nice Objective-C interop in regular macOS console applications, with regular host that is not aware that Objective-C interop exists. It is how we have the designed the Com/WinRT interop for .NET 5 and we are happy with it.
It is not clear whether we will be able to maintain this goal due to resource constrains. If we end up giving up on this goal and make the Objective-C interop work with just the Xamarin/Apple app host, I agree that this method is not really needed. We can just use PINVOKE_OVERRIDE
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They don't actually need to be implemented in native at all depending on what one wants to do with them.
I have seen the code in the tests of the feature/objc-interop branch but I had the feeling that it covers only narrow scenario. I was thinking specifically about the implementation in Xamarin that does translation of Objective-C exceptions. I may need to read more into it though.
Our original design goal was to make Objective-C interop work without special host.
I think this somewhat slipped off my mind and it's a valid point. On the other hand it feels like SetMessageSendCallback
specifically is problematic if two different libraries tried to use it. Likely not a big deal in practice but I remember seeing the concern in some comment earlier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking specifically about the implementation in Xamarin that does translation of Objective-C exceptions.
If one wants exceptions support, I agree that is likely not possible to implement in C#. However that should not be the reason for forcing it be a hard requirement.
On the other hand it feels like SetMessageSendCallback specifically is problematic if two different libraries tried to use it.
Yes that is true. This API is really a niche case for Xamarin at this point. The ComWrappers
also has an issue with singleton usage - the GC interaction model. It was a desire to make that more narrow but given Xamarin established guidance around usage of P/Invoke with Selectors it is hard to have a model that can work with multiple consumers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess my point was that if it's primarily for the Xamarin use-case then PINVOKE_OVERRIDE would work there just as good. It has the additional benefit of being slightly more flexible and guaranteeing the registration soon enough to ensure that all P/Invokes are consistently resolved (and thus it's easier to debug and reason about). Since there can only be one host it also takes care of the issue of two consumers trying to use the interface. Lastly, it's available on Mono and CoreCLR today so the implementation for both runtimes could be easily shared on the Xamarin side.
Obviously that was all under the assumption that one would have the control of the unmanaged host. If this is supposed to cover other scenarios like running under the default CoreCLR host (corehost, corerun or other) and possibly a managed implementation of the callbacks then I see the reason for the different approach.
I am still unsure what can you actually achieve in the managed code and how would you handle all the cases of parameter passing and forwarding that to the underlying implementation. The unit test doesn't seem to cover that ground.
@AaronRobinsonMSFT would it make sense to merge this as-is (or possibly close) if it's not an active PR? |
@danmoseley I can close it. We are close to getting to a place where the doc can be updated with the agreed upon solution but it will require another draft. I will resubmit when appropriate. |
One thing to consider is whether dotnet/designs is a better home. The pivot we agreed on is that the code-repo is appropriate if it's a living document that must be kept in sync with code; otherwise dotnet/designs is preferred. It doesn't need to be a cross-repo design, eg. |
@danmoseley Moved to dotnet/designs#197. |
See #44659